package controller

import (
	"errors"
	"fileNest/dao"
	"fileNest/global"
	"fileNest/model"
	"fileNest/service"
	"fileNest/storage"
	"fileNest/util"
	"fmt"
	"io"
	"mime"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"github.com/gin-gonic/gin"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

var baseDir = "./temp_uploads"

// 上传文件
func Upload(ctx *gin.Context) {
	fileHeader, err := ctx.FormFile("file")
	if err != nil {
		util.FailResponse(ctx, err)
		return
	}
	parentId := ctx.PostForm("parentId")
	tokenString := ctx.GetHeader("Authorization")
	if tokenString == "" {
		util.FailResponse(ctx, fmt.Errorf("token验证失败"), 401)
		return
	}
	_, userID, err := util.ParseJWT(tokenString)
	if err != nil {
		util.FailResponse(ctx, fmt.Errorf("token验证失败"), 401)
		return
	}

	var parentID *primitive.ObjectID = nil
	if parentId != "" {
		var parentid primitive.ObjectID
		if parentid, err = primitive.ObjectIDFromHex(parentId); err != nil {
			util.FailResponse(ctx, err)
			return
		}
		parentID = &parentid
	}

	if err = service.UploadFile(ctx, userID, parentID, fileHeader); err != nil {
		util.FailResponse(ctx, err)
		return
	}

	util.SuccessResponse(ctx, nil, "文件上传成功")
}

// 分片上传
func ChunksUpload(ctx *gin.Context) {
	tokenString := ctx.GetHeader("Authorization")
	_, userID, err := util.ParseJWT(tokenString)
	if err != nil {
		util.FailResponse(ctx, fmt.Errorf("token验证失败"), 401)
		return
	}
	fileHash := ctx.PostForm("fileHash")
	chunkIndex := ctx.PostForm("chunkIndex")
	totalChunks := ctx.PostForm("totalChunks")
	chunkHash := ctx.PostForm("chunkHash")
	if fileHash == "" || chunkHash == "" || chunkIndex == "" || totalChunks == "" {
		util.FailResponse(ctx, errors.New("缺少必传参数"), 400)
		return
	}
	chunk, err := ctx.FormFile("chunk")
	if err != nil {
		util.FailResponse(ctx, errors.New("缺少必传参数"), 400)
		return
	}

	chunkDir := filepath.Join(baseDir, userID, fileHash)
	if err := os.MkdirAll(chunkDir, os.ModePerm); err != nil {
		util.FailResponse(ctx, err)
		return
	}
	chunkPath := filepath.Join(chunkDir, fmt.Sprintf("chunk_%s", chunkIndex))
	if err := ctx.SaveUploadedFile(chunk, chunkPath); err != nil {
		util.FailResponse(ctx, err)
		return
	}
	util.SuccessResponse(ctx, nil, "上传成功")
}

// 合并文件切片
func UploadMerge(ctx *gin.Context) {
	var data model.Merge
	if err := ctx.ShouldBind(&data); err != nil {
		util.FailResponse(ctx, err)
		return
	}
	tokenString := ctx.GetHeader("Authorization")
	_, userID, err := util.ParseJWT(tokenString)
	if err != nil {
		util.FailResponse(ctx, fmt.Errorf("token验证失败"), 401)
		return
	}

	if userID == "" || data.FileHash == "" || data.FileName == "" || data.TotalChunks <= 0 {
		util.FailResponse(ctx, fmt.Errorf("缺少必要参数"), 400)
		return
	}

	chunkDir := filepath.Join(baseDir, userID, data.FileHash)

	// 检查所有切片是否齐全
	chunks := make([]string, 0, data.TotalChunks)
	for i := 0; i < data.TotalChunks; i++ {
		chunkPath := filepath.Join(chunkDir, fmt.Sprintf("chunk_%d", i))
		if _, err := os.Stat(chunkPath); err != nil {
			util.FailResponse(ctx, fmt.Errorf("缺少切片：chunk_%d", i))
			return
		}
		chunks = append(chunks, chunkPath)
	}

	// 合并切片到临时文件
	mergedFilePath := filepath.Join(chunkDir, "merged_"+data.FileName)
	mergedFile, err := os.Create(mergedFilePath)
	if err != nil {
		util.FailResponse(ctx, err)
		return
	}

	// 按顺序写入所有切片
	for _, chunkPath := range chunks {
		chunkFile, err := os.Open(chunkPath)
		if err != nil {
			util.FailResponse(ctx, err)
			return
		}
		_, err = io.Copy(mergedFile, chunkFile)
		chunkFile.Close()
		if err != nil {
			util.FailResponse(ctx, err)
			return
		}
	}

	var storagePath string
	if data.ParentID != nil {
		storagePath = fmt.Sprintf("%s/%s/%s", userID, data.ParentID.Hex(), data.FileName)
	} else {
		storagePath = fmt.Sprintf("%s/%s", userID, data.FileName)
	}

	// 上传合并文件到MinIO
	if err := storage.AddMinioFileFromLocal(global.BucketName, storagePath, mergedFilePath); err != nil {
		util.FailResponse(ctx, err)
		return
	}

	info, err := mergedFile.Stat()
	size := uint64(info.Size())
	now := time.Now()
	ext := filepath.Ext(mergedFilePath)
	mimeType := mime.TypeByExtension(ext)
	if mimeType == "" {
		// 读取文件头部字节精准判断
		file, err := os.Open(mergedFilePath)
		if err != nil {
			// 处理错误
		}
		defer file.Close()
		buffer := make([]byte, 512)
		_, err = file.Read(buffer)
		if err != nil {
			// 处理错误
		}
		mimeType = http.DetectContentType(buffer)
	}

	id := primitive.NewObjectID()
	entry := model.FileEntry{
		ID:          &id,
		UserID:      userID,
		ParentID:    data.ParentID,
		Name:        data.FileName,
		Size:        size,
		MimeType:    mimeType,
		IsDir:       false,
		StoragePath: storagePath,
		CreatedAt:   &now,
	}

	if _, err = dao.DBAdd("entries", entry); err != nil {
		util.FailResponse(ctx, err)
		return
	}
	// 清理临时切片和合并文件
	defer func() {
		if err := mergedFile.Close(); err != nil {
			fmt.Errorf("关闭合并文件失败：", err)
		}
		if err := os.RemoveAll(chunkDir); err != nil {
			fmt.Errorf("删除临时目录失败：", err)
		}
	}()

	util.SuccessResponse(ctx, nil, "上传成功")
}
